package com.chengqs.summer.context;

import com.chengqs.summer.annotation.*;
import com.chengqs.summer.exception.BeanCreationException;
import com.chengqs.summer.exception.BeanDefinitionException;
import com.chengqs.summer.exception.BeanNotOfRequiredTypeException;
import com.chengqs.summer.exception.NoUniqueBeanDefinitionException;
import com.chengqs.summer.io.PropertyResolver;
import com.chengqs.summer.io.ResourceResolver;
import com.chengqs.summer.utils.ClassUtils;
import jakarta.annotation.Nullable;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 这个 AnnotationConfigApplicationContext 类实现了一个基于注解的Bean容器，它提供了以下主要功能：<br>
 *
 * <p>扫描组件：通过 @ComponentScan 和 @Import 扫描配置类中的组件。</p>
 * <p>创建Bean定义：为符合条件的类和方法创建Bean定义。</p>
 * <p>查找Bean定义：提供多种方法根据名字或类型查找Bean定义。</p>
 * <p>处理依赖关系：在Bean定义过程中，支持 @Primary 注解来处理多重候选问题。</p>
 *
 * <p>该类负责扫描指定的包路径中的所有类，识别出符合条件的组件，并将其注册为 Spring 管理的 Bean。
 * 它通过一系列的设计模式来实现其核心功能，包括：
 *
 * <ul>
 *   <li>工厂模式（Factory Pattern）：该类通过反射机制创建和管理 Bean 的实例，而不是通过直接调用构造函数。</li>
 *   <li>单例模式（Singleton Pattern）：Spring 容器默认管理的 Bean 是单例的，确保在整个应用程序中只存在一个实例。</li>
 *   <li>策略模式（Strategy Pattern）：该类可以根据不同的注解策略灵活地处理 Bean 的创建和初始化过程。</li>
 *   <li>模板方法模式（Template Method Pattern）：通过定义组件扫描和 Bean 定义的基本流程，允许子类进行扩展和自定义实现。</li>
 *   <li>责任链模式（Chain of Responsibility Pattern）：在 Bean 定义过程中通过多种检查和处理机制，逐步处理复杂的依赖关系。</li>
 *   <li>观察者模式（Observer Pattern）：在 Bean 生命周期中支持事件机制，通过注解实现特定阶段的初始化和销毁逻辑。</li>
 *   <li>装饰器模式（Decorator Pattern）：通过注解和 AOP 技术动态增强 Bean 的功能，而无需修改其内部结构。</li>
 * </ul>
 */
public class AnnotationConfigApplicationContext {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    // 用于存储所有Bean的定义（BeanDefinition），键是Bean的名字，值是对应的Bean定义。
    protected final Map<String, BeanDefinition> beans;
    // 属性解析器，用于解析配置属性。这在配置Bean时可能需要使用。
    protected final PropertyResolver propertyResolver;

    /**
     * 构造函数，初始化属性解析器，并扫描获取所有Bean的Class类型
     * @param configClass 用来提供配置类，通常标有 @Configuration 注解，用于定义Bean。
     * @param propertyResolver 用于解析配置文件中的属性。
     */
    public AnnotationConfigApplicationContext(Class<?> configClass, PropertyResolver propertyResolver) {
        this.propertyResolver = propertyResolver;
        // 扫描获取所有Bean的Class类型:
        final Set<String> beanClassNames = scanForClassNames(configClass);
        // 创建Bean的定义:
        this.beans = createBeanDefinitions(beanClassNames);
    }

    /**
     * 根据扫描的ClassName创建BeanDefinition
     * @param classNameSet 从 scanForClassNames 方法获取到的所有Class名。
     * @return 返回一个Map，其中包含了所有的Bean定义。
     */
    Map<String, BeanDefinition> createBeanDefinitions(Set<String> classNameSet) {
        // 创建一个map集合存储Bean定义
        Map<String, BeanDefinition> defs = new HashMap<>();
        for (String className : classNameSet) {
            // 遍历每一个Class名，尝试加载Class对象。
            Class<?> clazz = null;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new BeanCreationException(e);
            }
            if (clazz.isAnnotation() || clazz.isEnum() || clazz.isInterface() || clazz.isRecord()) {
                // 跳过注解类型、枚举类型、接口以及Java 14引入的记录类型。
                continue;
            }
            // 如果一个类标注了 @Component 注解，则尝试创建一个 BeanDefinition。
            Component component = ClassUtils.findAnnotation(clazz, Component.class);
            if (component != null) {
                logger.atDebug().log("found component: {}", clazz.getName());
                // 获取类的权限修饰符
                int mod = clazz.getModifiers();
                if (Modifier.isAbstract(mod)) {
                    // 如果是抽象类，则抛出异常
                    throw new BeanDefinitionException("@Component class " + clazz.getName() + " must not be abstract.");
                }
                if (Modifier.isPrivate(mod)) {
                    // 如果是私有类，则抛出异常
                    throw new BeanDefinitionException("@Component class " + clazz.getName() + " must not be private.");
                }

                // 获取Bean的名字
                String beanName = ClassUtils.getBeanName(clazz);
                // 创建 BeanDefinition:
                BeanDefinition def = new BeanDefinition(
                        beanName,
                        clazz,
                        // 获取合适的构造函数
                        getSuitableConstructor(clazz),
                        // 获取Bean的顺序
                        getOrder(clazz),
                        // 检查是否有 @Primary 注解。
                        clazz.isAnnotationPresent(Primary.class),
                        // named init
                        null,
                        // destroy method:
                        null,
                        // 获取初始化方法
                        ClassUtils.findAnnotationMethod(clazz, PostConstruct.class),
                        // 获取销毁方法
                        ClassUtils.findAnnotationMethod(clazz, PreDestroy.class));
                addBeanDefinitions(defs, def);
                logger.atDebug().log("define bean: {}", def);

                // 如果类有 @Configuration 注解，则调用 scanFactoryMethods 方法，扫描工厂方法（带 @Bean 注解的方法）。
                Configuration configuration = ClassUtils.findAnnotation(clazz, Configuration.class);
                if (configuration != null) {
                    scanFactoryMethods(beanName, clazz, defs);
                }
            }
        }
        return defs;
    }

    /**
     * 获取公共构造函数或非公共构造函数作为后备。
     * @param clazz 类对象，用于获取其构造函数。
     * @return 返回找到的构造函数。
     */
    Constructor<?> getSuitableConstructor(Class<?> clazz) {
        // 获取所有公共构造函数
        Constructor<?>[] cons = clazz.getConstructors();
        if (cons.length == 0) {
            // 如果没有公共构造函数，则获取所有构造函数
            cons = clazz.getDeclaredConstructors();
            if (cons.length != 1) {
                // 如果找到的构造函数不唯一，则抛出异常。
                throw new BeanDefinitionException("More than one constructor found in class " + clazz.getName() + ".");
            }
        }
        if (cons.length != 1) {
            // 如果类有多个公共构造函数，则抛出异常。
            throw new BeanDefinitionException("More than one public constructor found in class " + clazz.getName() + ".");
        }
        // 返回构造函数
        return cons[0];
    }

    /**
     * 扫描类中所有方法，查找带有 @Bean 注解的方法。
     * @param factoryBeanName 工厂Bean的名称，用于在Bean定义中标记创建此Bean的工厂类。
     * @param clazz 需要扫描的配置类，用于查找带 @Bean 注解的方法。
     * @param defs 存储Bean定义的Map。
     */
    void scanFactoryMethods(String factoryBeanName, Class<?> clazz, Map<String, BeanDefinition> defs) {
        // 获取所有方法
        for (Method method : clazz.getDeclaredMethods()) {
            // 获取 @Bean 注解
            Bean bean = method.getAnnotation(Bean.class);
            if (bean != null) {
                // 获取权限修饰符
                int mod = method.getModifiers();
                if (Modifier.isAbstract(mod)) {
                    // 如果是抽象方法，则抛出异常
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not be abstract.");
                }
                if (Modifier.isFinal(mod)) {
                    // 如果是final方法，则抛出异常
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not be final.");
                }
                if (Modifier.isPrivate(mod)) {
                    // 如果是私有方法，则抛出异常
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not be private.");
                }
                // 获取返回值类型
                Class<?> beanClass = method.getReturnType();
                if (beanClass.isPrimitive()) {
                    // 如果返回值是基本类型，则抛出异常。
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not return primitive type.");
                }
                if (beanClass == void.class || beanClass == Void.class) {
                    // 如果返回值是void，则抛出异常。
                    throw new BeanDefinitionException("@Bean method " + clazz.getName() + "." + method.getName() + " must not return void.");
                }
                BeanDefinition def = new BeanDefinition(
                        ClassUtils.getBeanName(method),
                        beanClass,
                        factoryBeanName,
                        method,
                        getOrder(method),
                        method.isAnnotationPresent(Primary.class),
                        // 获取初始化方法:
                        bean.initMethod().isEmpty() ? null : bean.initMethod(),
                        // 获取销毁方法
                        bean.destroyMethod().isEmpty() ? null : bean.destroyMethod(),
                        // @PostConstruct / @PreDestroy method:
                        null, null);
                // 加入到Bean定义的Map中
                addBeanDefinitions(defs, def);
                logger.atDebug().log("define bean: {}", def);
            }
        }
    }

    /**
     * 将Bean定义添加到Map中。
     * @param defs 存储Bean定义的Map。
     * @param def 待添加的Bean定义。
     */
    void addBeanDefinitions(Map<String, BeanDefinition> defs, BeanDefinition def) {
        if (defs.put(def.getName(), def) != null) {
            // 如果存在重复的Bean名，则抛出 BeanDefinitionException 异常。
            throw new BeanDefinitionException("Duplicate bean name: " + def.getName());
        }
    }

    /**
     * 获取类的顺序值，如果没有标注 @Order 注解，则返回默认的最大值。
     * @param clazz 用于获取其上的 @Order 注解。
     * @return 返回顺序值。
     */
    int getOrder(Class<?> clazz) {
        Order order = clazz.getAnnotation(Order.class);
        return order == null ? Integer.MAX_VALUE : order.value();
    }

    /**
     * 获取方法的顺序值，如果没有标注 @Order 注解，则返回默认的最大值。
     * @param method 方法对象，用于获取其上的 @Order 注解。
     * @return 返回顺序值。
     */
    int getOrder(Method method) {
        Order order = method.getAnnotation(Order.class);
        return order == null ? Integer.MAX_VALUE : order.value();
    }

    /**
     * 扫描配置类中的所有Class名。
     * @param configClass 配置类，用于扫描组件的包名。
     * @return 返回扫描到的Class名。
     */
    protected Set<String> scanForClassNames(Class<?> configClass) {
        // 获取ComponentScan注解
        ComponentScan scan = ClassUtils.findAnnotation(configClass, ComponentScan.class);
        // 获取包名
        // ComponentScan为null或这个属性为空，则使用配置类的包名。
        final String[] scanPackages = scan == null || scan.value().length == 0 ? new String[] { configClass.getPackage().getName() } : scan.value();
        logger.atInfo().log("component scan in packages: {}", Arrays.toString(scanPackages));

        // 创建类名集合
        Set<String> classNameSet = new HashSet<>();
        for (String pkg : scanPackages) {
            // 扫描package:
            logger.atDebug().log("scan package: {}", pkg);
            // 构造资源解析器
            ResourceResolver rr = new ResourceResolver(pkg);
            // 获取所有类的名字
            List<String> classList = rr.scan(res -> {
                String name = res.name();
                if (name.endsWith(".class")) {
                    return name.substring(0, name.length() - 6).replace("/", ".").replace("\\", ".");
                }
                return null;
            });
            if (logger.isDebugEnabled()) {
                classList.forEach((className) -> {
                    logger.debug("class found by component scan: {}", className);
                });
            }
            // 添加到集合中
            classNameSet.addAll(classList);
        }

        // 查找@Import(Xyz.class):
        Import importConfig = configClass.getAnnotation(Import.class);
        if (importConfig != null) {
            for (Class<?> importConfigClass : importConfig.value()) {
                // 遍历并获取类名
                String importClassName = importConfigClass.getName();
                if (classNameSet.contains(importClassName)) {
                    logger.warn("ignore import: " + importClassName + " for it is already been scanned.");
                } else {
                    logger.debug("class found by import: {}", importClassName);
                    classNameSet.add(importClassName);
                }
            }
        }
        return classNameSet;
    }

    /**
     * 判断一个Bean定义是否是一个配置类，即检查其上是否标注了 @Configuration 注解。
     * @param def Bean定义。
     * @return 返回是否是一个配置类。
     */
    boolean isConfigurationDefinition(BeanDefinition def) {
        return ClassUtils.findAnnotation(def.getBeanClass(), Configuration.class) != null;
    }

    /**
     * 根据Name查找BeanDefinition，如果Name不存在，返回null
     * @param name Bean的名字。
     * @return 返回BeanDefinition。
     */
    @Nullable
    public BeanDefinition findBeanDefinition(String name) {
        return this.beans.get(name);
    }

    /**
     * 根据Name和Type查找BeanDefinition，如果Name不存在，返回null，如果Name存在，但Type不匹配，抛出异常。
     * @param name Bean的名字。
     * @param requiredType 所需的类型。
     * @return 返回BeanDefinition。
     */
    @Nullable
    public BeanDefinition findBeanDefinition(String name, Class<?> requiredType) {
        BeanDefinition def = findBeanDefinition(name);
        if (def == null) {
            return null;
        }
        if (!requiredType.isAssignableFrom(def.getBeanClass())) {
            throw new BeanNotOfRequiredTypeException(String.format("Autowire required type '%s' but bean '%s' has actual type '%s'.", requiredType.getName(),
                    name, def.getBeanClass().getName()));
        }
        return def;
    }

    /**
     * 根据Type查找若干个BeanDefinition，返回0个或多个。
     * @param type 所需的类型。
     * @return 返回BeanDefinition列表。
     */
    public List<BeanDefinition> findBeanDefinitions(Class<?> type) {
        return this.beans.values().stream()
                // filter by type and sub-type:
                .filter(def -> type.isAssignableFrom(def.getBeanClass()))
                // 排序:
                .sorted().collect(Collectors.toList());
    }

    /**
     * 根据Type查找某个BeanDefinition，如果不存在返回null，如果存在多个返回@Primary标注的一个，
     * 如果有多个@Primary标注，或没有@Primary标注但找到多个，均抛出NoUniqueBeanDefinitionException
     * @param type 所需的类型。
     * @return 返回BeanDefinition。
     */
    @Nullable
    public BeanDefinition findBeanDefinition(Class<?> type) {
        // 查找符合给定类型的Bean定义。
        List<BeanDefinition> defs = findBeanDefinitions(type);
        if (defs.isEmpty()) {
            // 如果不存在返回 null。
            return null;
        }
        if (defs.size() == 1) {
            // 如果存在一个，返回该Bean定义。
            return defs.get(0);
        }
        // 如果存在多个，查找 @Primary 注解的Bean定义。
        List<BeanDefinition> primaryDefs = defs.stream().filter(def -> def.isPrimary()).collect(Collectors.toList());
        if (primaryDefs.size() == 1) {
            return primaryDefs.get(0);
        }
        // 如果有多个 @Primary 注解或没有 @Primary 注解但找到多个Bean定义，抛出 NoUniqueBeanDefinitionException。
        if (primaryDefs.isEmpty()) {
            throw new NoUniqueBeanDefinitionException(String.format("Multiple bean with type '%s' found, but no @Primary specified.", type.getName()));
        } else {
            throw new NoUniqueBeanDefinitionException(String.format("Multiple bean with type '%s' found, and multiple @Primary specified.", type.getName()));
        }
    }
}
